"""Music management service."""
from typing import List, Optional, Dict, Any
from datetime import datetime
import uuid
from app import db
from app.models.music import Music
from app.models.user import User
from app.models.walking_session import WalkingSession
from app.models.poi import POI


class MusicService:
    """Service for managing music library and metadata."""
    
    def save_music(
        self,
        user_id: str,
        file_url: str,
        duration: int,
        session_id: Optional[str] = None,
        poi_id: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Music:
        """
        Save music to user's library with metadata.
        
        Args:
            user_id: User ID who owns the music
            file_url: URL to the music file
            duration: Duration in seconds
            session_id: Optional walking session ID
            poi_id: Optional POI ID associated with the music
            metadata: Optional metadata dict (time, location, context, etc.)
            
        Returns:
            Music: Saved music record
            
        Raises:
            ValueError: If user doesn't exist
        """
        # Verify user exists
        user = User.query.get(user_id)
        if not user:
            raise ValueError(f"User {user_id} not found")
        
        # Verify session exists if provided
        if session_id:
            session = WalkingSession.query.get(session_id)
            if not session:
                raise ValueError(f"Session {session_id} not found")
        
        # Verify POI exists if provided
        if poi_id:
            poi = POI.query.get(poi_id)
            if not poi:
                raise ValueError(f"POI {poi_id} not found")
        
        # Prepare metadata with timestamp and location
        meta_info = metadata or {}
        if 'generated_at' not in meta_info:
            meta_info['generated_at'] = datetime.utcnow().isoformat()
        
        # Add location from session if available
        if session_id:
            session = WalkingSession.query.get(session_id)
            if session and session.current_latitude is not None and session.current_longitude is not None:
                meta_info['location'] = {
                    'latitude': session.current_latitude,
                    'longitude': session.current_longitude
                }
        
        # Add POI name if available
        if poi_id:
            poi = POI.query.get(poi_id)
            if poi:
                meta_info['poi_name'] = poi.name
        
        # Create music record with generated UUID
        music = Music(
            id=str(uuid.uuid4()),
            user_id=user_id,
            session_id=session_id,
            poi_id=poi_id,
            file_url=file_url,
            duration=duration,
            meta_info=meta_info
        )
        
        db.session.add(music)
        db.session.commit()
        
        return music
    
    def get_user_music_library(
        self,
        user_id: str,
        limit: Optional[int] = None,
        offset: int = 0
    ) -> List[Music]:
        """
        Get user's music library.
        
        Args:
            user_id: User ID
            limit: Optional limit on number of results
            offset: Offset for pagination
            
        Returns:
            List[Music]: List of music records ordered by creation time (newest first)
        """
        query = Music.query.filter_by(user_id=user_id).order_by(Music.created_at.desc())
        
        if limit:
            query = query.limit(limit).offset(offset)
        
        return query.all()
    
    def get_music_by_id(self, music_id: str, user_id: str) -> Optional[Music]:
        """
        Get a specific music record by ID.
        
        Args:
            music_id: Music ID
            user_id: User ID (for authorization check)
            
        Returns:
            Optional[Music]: Music record if found and belongs to user, None otherwise
        """
        music = Music.query.filter_by(id=music_id, user_id=user_id).first()
        return music
    
    def get_session_music(self, session_id: str) -> List[Music]:
        """
        Get all music generated during a specific session.
        
        Args:
            session_id: Walking session ID
            
        Returns:
            List[Music]: List of music records from the session
        """
        return Music.query.filter_by(session_id=session_id).order_by(Music.created_at).all()
    
    def delete_music(self, music_id: str, user_id: str) -> bool:
        """
        Delete a music record.
        
        Args:
            music_id: Music ID
            user_id: User ID (for authorization check)
            
        Returns:
            bool: True if deleted, False if not found or unauthorized
        """
        music = Music.query.filter_by(id=music_id, user_id=user_id).first()
        if not music:
            return False
        
        db.session.delete(music)
        db.session.commit()
        return True
    
    def generate_music_url(self, music: Music, base_url: str = "") -> str:
        """
        Generate full URL for music file.
        
        Args:
            music: Music record
            base_url: Optional base URL (e.g., CDN domain)
            
        Returns:
            str: Full URL to music file
        """
        # If file_url is already a full URL, return as-is
        if music.file_url.startswith('http://') or music.file_url.startswith('https://'):
            return music.file_url
        
        # Otherwise, prepend base URL
        if base_url:
            return f"{base_url.rstrip('/')}/{music.file_url.lstrip('/')}"
        
        return music.file_url
    
    def update_music_metadata(
        self,
        music_id: str,
        user_id: str,
        metadata: Dict[str, Any]
    ) -> Optional[Music]:
        """
        Update music metadata.
        
        Args:
            music_id: Music ID
            user_id: User ID (for authorization check)
            metadata: New metadata to merge with existing
            
        Returns:
            Optional[Music]: Updated music record if found, None otherwise
        """
        music = Music.query.filter_by(id=music_id, user_id=user_id).first()
        if not music:
            return None
        
        # Merge new metadata with existing
        current_meta = music.meta_info or {}
        current_meta.update(metadata)
        music.meta_info = current_meta
        
        db.session.commit()
        return music
    
    def _get_current_timestamp(self) -> str:
        """Get current timestamp as ISO format string."""
        return datetime.utcnow().isoformat()
